<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  

  
  <title>蔡进东的日志</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="不要在最该奋斗的年纪去偷懒!">
<meta property="og:type" content="website">
<meta property="og:title" content="蔡进东的日志">
<meta property="og:url" content="http://cai4633.github.io/index.html">
<meta property="og:site_name" content="蔡进东的日志">
<meta property="og:description" content="不要在最该奋斗的年纪去偷懒!">
<meta property="og:locale" content="zh-CN">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="蔡进东的日志">
<meta name="twitter:description" content="不要在最该奋斗的年纪去偷懒!">
  
  
    <link rel="icon" href="/favicon.png">
  
  
    <link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
  
  <link rel="stylesheet" href="/css/style.css">
</head>
</html>
<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">蔡进东的日志</a>
      </h1>
      
      <h2 id="subtitle-wrap">
        <a href="/" id="subtitle">不要在最该奋斗的年纪选择去偷懒，只有度过一段连自己都被感动了的日子，才能变成那个最好的自己.</a>
      </h2>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
        <a class="main-nav-link" href="/">Home</a>
        
        <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
        <a id="nav-search-btn" class="nav-icon" title="搜索"></a>
      </nav>
      <div class="local-search local-search-plugin">
        <input type="search" placeholder="站内搜索" id="local-search-input" class="local-search-input-cls" />
        <div id="local-search-result" class="local-search-result-cls"></div>
      </div>
      <!-- <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="http://cai4633.github.io"></form>
      </div> -->
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main">
  
    <article id="post-前端鉴权总结" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2021/01/08/前端鉴权总结/" class="article-date">
  <time datetime="2021-01-08T03:17:13.000Z" itemprop="datePublished">2021-01-08</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/前端/">前端</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/01/08/前端鉴权总结/">前端鉴权总结</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>我们在访问网站时不可避免会遇到登录的问题，这归根到底就是访问权限的问题。前端鉴权方案主要有以下几种：</p>
<ol>
<li>HTTP Basic Authentication：现在用的较少，主要用在一些较老的项目中。</li>
<li>session-cookie：依赖 cookie,只适用于 web 系统。</li>
<li>token: 目前主流鉴权方式，适用于多种客户端（浏览器、app 等）。</li>
<li>OAuth：第三方授权，方便快捷是以后的趋势。</li>
</ol>
<h2 id="一、HTTP-Basic-Authentication"><a href="#一、HTTP-Basic-Authentication" class="headerlink" title="一、HTTP Basic Authentication"></a>一、HTTP Basic Authentication</h2><p>顾名思义，这种鉴权方式叫做 http 基本认证。<br><img src="/images/http_basic_authentication.png" alt="HTTP Basic Authentication 鉴权流程"></p>
<h3 id="鉴权流程"><a href="#鉴权流程" class="headerlink" title="鉴权流程"></a>鉴权流程</h3><ol>
<li>客户端发送 http 请求给服务器，服务器验证有无访问权限；</li>
<li>无访问权限时，服务器<strong>返回 401</strong>，客户端接收到 401 响应自动弹出登录验证对话框；</li>
<li>用户输入用户名和密码后，客户端将用户名和密码以 <code>name: password</code> 的形式采用 <code>base64</code> 编码方式编码，并放入请求头，再次发送请求；</li>
<li>服务器校验成功，返回数据。</li>
</ol>
<h3 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h3><p>这种鉴权方式的使用简单但安全性较差。配合 https 可以在<strong>内部网络</strong>，或者对安全要求不是很高的网络中使用，比较适合<strong>只需要一个特定密码</strong>就可以访问的场景。</p>
<h2 id="二、session-cookie"><a href="#二、session-cookie" class="headerlink" title="二、session-cookie"></a>二、session-cookie</h2><p>session 是此方案区别其他方式的核心。cookie 只是信息传递的<strong>载体</strong>。整个验证流程如下：</p>
<ol>
<li>浏览器登录时发送账号密码到服务端，服务端查询 User 库，校验用户</li>
<li>校验成功后，服务端把用户<strong>登录状态存到 session 中</strong>，同时生成一个对应的 sessionId 由登录接口返回，把 <strong>sessionId 存储到 cookie</strong>上</li>
<li>浏览器再次发送业务请求时，带有 sessionId 的 <strong>cookie 会自动添加到 Request Header 中</strong>发送到服务端</li>
<li>服务端通过 sessionId 在 session 库中查询，<strong>校验登录状态</strong></li>
<li>检验成功后，服务端<strong>进行业务处理并返回结果</strong><br><img src="/images/session_cookie.jpg" alt="session-cookie 鉴权流程"></li>
</ol>
<h3 id="session-的存储方式"><a href="#session-的存储方式" class="headerlink" title="session 的存储方式"></a>session 的存储方式</h3><ol>
<li>直接通过变量引用存储在<strong>服务器本地内存</strong>，缺点是服务器重启后，session 就会消失。</li>
<li>存储在<strong>普通数据库</strong>。缺点是性能不高。</li>
<li>（<code>推荐</code>）存储在<strong>Redis</strong>(内存型数据库)中。<strong>访问快，可持久存储</strong>。</li>
</ol>
<h3 id="分布式-session"><a href="#分布式-session" class="headerlink" title="分布式 session"></a>分布式 session</h3><p>大型应用的网络请求一般会采用负载均衡的优化方式。当业务请求和登录请求到达的服务器不是同一个时，普通的 session-cookie 鉴权就会出现 session 验证失败的问题！通常的解决方案如下：</p>
<ol>
<li>（<code>推荐</code>）从「存储」的角度来看，把 session 集中存储在独立的 Redis 或普通数据库中，就可以把 session 都存到一个库里。</li>
<li>从「分布」的角度来看，让相同 IP 的请求在负载均衡时都发送到同一台服务器上。以 nginx 为例，可以配置 ip_hash 来实现。</li>
</ol>
<h3 id="session-cookie-登录状态的有效期"><a href="#session-cookie-登录状态的有效期" class="headerlink" title="session-cookie 登录状态的有效期"></a>session-cookie 登录状态的有效期</h3><p>登录状态一般需要设置一定的有效期，采用 session-cookie 方案，一般可以从两个方面设置有效期：</p>
<ol>
<li>在 cookie 上设置。服务器返回 sessionId 是通过 <strong>set-cookie</strong> 响应头在浏览器上种下 cookie。此时就可以通过 cookie 属性 <strong>Expires / Max-Age</strong> 设置过期时间。</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Set-Cookie: sid=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure;HttpOnly;path=/blog;Domain=baidu.com;Max-Age=240000</span><br></pre></td></tr></table></figure>

<ol start="2">
<li>在 session 上设置。服务器可以设置一段时间后<strong>删除 session</strong>，sessionId 校验查询不到 对应的 session 就相当于登录状态失效了。</li>
</ol>
<h2 id="三、token（推荐）"><a href="#三、token（推荐）" class="headerlink" title="三、token（推荐）"></a>三、token（<code>推荐</code>）</h2><p>session-cookie 方案通常是基于 cookie 的。而 <strong>cookie 只有浏览器才有</strong>，对于 app 应用，此方案有天生的缺陷。而且 session-cookie 方案<strong>需要内存存储</strong> session 和数据库查询, 加大了服务器负担。为了解决这些问题，token 鉴权方案应势而生。</p>
<ol>
<li>浏览器登录时发送账号密码到服务端，服务端查询 User 库，校验用户</li>
<li>校验成功后获得用户信息，把<strong>用户信息和 token 配置</strong>编码加密生成 token，通过 cookie set 到浏览器（或者通过<strong>响应头</strong>）</li>
<li>用户再次请求业务接口时，通过 cookie 携带 token（或者通过<strong>请求头</strong>）发送到服务端</li>
<li>服务器校验 token 有效性，进行业务逻辑处理并返回数据<br><img src="/images/token.jpg" alt="token 鉴权"><br><img src="/images/access_and_refresh_token.jpg" alt="更复杂的 token 鉴权（access token and refresh token）"></li>
</ol>
<h3 id="JWT（json-web-token）"><a href="#JWT（json-web-token）" class="headerlink" title="JWT（json web token）"></a>JWT（json web token）</h3><p>token 里面包含了一些用户信息，所以安全性就有一定的要求。token 加密的方法和信息传递的形式有很多种，其中业内采用较多的标准就是 jwt。它分为三部分，第一部分 header 是加密算法声明，第二部分 payload 是用户信息和一些数据，第三部分 signature 是签名。<br><img src="/images/jwt.jpg" alt="jwt"></p>
<h2 id="四、OAuth-第三方登录"><a href="#四、OAuth-第三方登录" class="headerlink" title="四、OAuth 第三方登录"></a>四、OAuth 第三方登录</h2><p>OAuth 2.0 是<strong>目前最流行</strong>的授权机制，用来授权第三方应用，获取用户数据。数据的所有者告诉系统，同意授权第三方应用进入系统，获取这些数据。系统从而产生一个短期的可进入令牌（token），用来代替密码，供第三方应用使用。OAuth2.0 有四种授权方式：</p>
<ol>
<li><p>授权码 code 式 （<code>推荐</code>）</p>
</li>
<li><p>隐藏式 （忽略请求授权码步骤，适用<strong>纯前端应用</strong>）</p>
</li>
<li><p>密码式 （只有高度可信任应用才使用，不安全，<code>不推荐</code>）</p>
</li>
<li><p>凭证式 （适用于没有前端的<strong>命令行应用</strong>）</p>
<p><img src="/images/oauth.png" alt="Oauth 第三方登录授权码方式"></p>
</li>
</ol>
<p>现在我们主要总结一下 <strong>授权码式</strong> 的授权逻辑：</p>
<ol>
<li>首先我们需要在 B 网站 （QQ、微信或者 github 等）上登记 A 网站信息，主要包括 A 网站的<strong>homepage_url 和 redirect_uri</strong> 等信息。登记后，B 网站会返回 <strong>client ID 和 client secret</strong> 用于后续请求的身份验证。</li>
<li>当我们登录 A 网站时，选择第三方授权。浏览器会携带<strong>client_id、redirect_uri</strong>等信息跳转到 B 网站授权页面，B 网站要求我们登录验证信息。（扫码或者输入密码）</li>
<li>用户登录 B 网站后，B 网站校验用户信息并携带 <code>code</code> 重定向到 <strong>redirect_uri</strong></li>
<li>A 网站后端接收到 code 后，再次携带 grant_type、client_id 、client_secret、code、redirect_uri 等信息向 B 网站服务器<strong>请求 token</strong></li>
<li>B 网站后端校验 code 等信息，再次向 redirect_uri 返回<strong>access token 和 refresh token</strong></li>
<li>A 网站服务器拿到 token 后就会根据自身需求处理相关业务逻辑或者重定向到 A 网站首页</li>
</ol>
<h2 id="参考文章："><a href="#参考文章：" class="headerlink" title="参考文章："></a>参考文章：</h2><ul>
<li><a href="https://zhuanlan.zhihu.com/p/281414244" target="_blank" rel="noopener">前端鉴权的兄弟们：cookie、session、token、jwt、单点登录</a></li>
<li><a href="http://www.ruanyifeng.com/blog/2019/04/oauth_design.html" target="_blank" rel="noopener">OAuth 2.0 的一个简单解释</a></li>
<li><a href="http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html" target="_blank" rel="noopener">OAuth 2.0 的四种方式</a></li>
<li><a href="http://www.ruanyifeng.com/blog/2019/04/github-oauth.html" target="_blank" rel="noopener">GitHub OAuth 第三方登录示例教程</a></li>
</ul>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2021/01/08/前端鉴权总结/" data-id="ckjqth4sf002q2gw0fggoxo9r" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/session-cookie/">session-cookie</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/token/">token</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/鉴权/">鉴权</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-web性能优化" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2021/01/03/web性能优化/" class="article-date">
  <time datetime="2021-01-03T01:22:12.000Z" itemprop="datePublished">2021-01-03</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/前端/">前端</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/01/03/web性能优化/">web性能优化</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p><img src="/images/web_performance_optimization.png" alt><br>一个前端开发必定绕不开<strong>性能优化</strong>这个话题。web 性能优化通俗的讲就是让页面尽快呈现在用户眼前。要想解决性能优化这个难题必须提前了解<a href="/2020/04/19/浏览器输入URL到页面显示全过程/">浏览器从输入 URL 到渲染页面的整个过程</a>。简单的来说 web 性能优化主要从以下几个方面入手：</p>
<ol>
<li>DNS 查询过程</li>
<li>HTTP 请求过程</li>
<li>资源下载过程</li>
<li>浏览器渲染过程</li>
<li>缓存过程</li>
</ol>
<h2 id="一、DNS-查询优化"><a href="#一、DNS-查询优化" class="headerlink" title="一、DNS 查询优化"></a>一、DNS 查询优化</h2><p>浏览器地址栏输入 URL 发送出去后，域名会通过 DNS 服务器进行域名解析并返回对应的 IP 地址。这个过程通常比较快，但是也可以有优化的空间。</p>
<h3 id="DNS-缓存"><a href="#DNS-缓存" class="headerlink" title="DNS 缓存"></a>DNS 缓存</h3><p>DNS 缓存一般都是浏览器等自动缓存的。但是了解其相应的过程还是必要的。DNS 解析首先查询<strong>浏览器缓存</strong>，然后查询操作系统<strong>HOSTS 文件</strong>，再查询<strong>路由器缓存</strong>，如果无法找到缓存才正式向<strong>本地 DNS 服务器</strong> 或者<strong>根服务器</strong>逐级请求解析。这其中我们可以<strong>更改 hosts</strong> 加快解析速度，但是我们不能指望每个用户都会改 hosts。</p>
<h3 id="负载均衡"><a href="#负载均衡" class="headerlink" title="负载均衡"></a>负载均衡</h3><p>如果同一台服务器同时链接的用户数过大时，服务器可能会崩溃。解决办法就是 DNS 负载均衡技术。原理是在 DNS 服务器中为同一个域名配置多个 IP 地址,在应答 DNS 查询时,根据服务器的负载量以及离用户的距离等返回不同的解析结果,将客户端的访问引导到不同的服务器上,从而达到负载均衡的目的｡例如 <strong>CDN 节点</strong>。</p>
<h3 id="DNS-预获取"><a href="#DNS-预获取" class="headerlink" title="DNS 预获取"></a>DNS 预获取</h3><p>现代浏览器默认会对页面中和当前域名（正在浏览网页的域名）不在同一个域的域名进行预获取，并且缓存结果，这就是隐式的 DNS Prefetch。如果想对页面中没有出现的域进行预获取，那么就要使用显式的 <strong>dns-prefetch</strong> 了。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 打开dns-prefetch --&gt;</span><br><span class="line">&lt;meta http-equiv=&quot;x-dns-prefetch-control&quot; content=&quot;on&quot;&gt;</span><br><span class="line">&lt;link rel=&quot;dns-prefetch&quot; href=&quot;//www.baidu.com&quot;&gt;</span><br><span class="line">&lt;link rel=&quot;dns-prefetch&quot; href=&quot;//zhihu.com&quot;&gt;</span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 关闭dns-prefetch 禁止隐式dns预获取 --&gt;</span><br><span class="line">&lt;meta http-equiv=&quot;x-dns-prefetch-control&quot; content=&quot;off&quot;&gt;</span><br></pre></td></tr></table></figure>

<h2 id="二、http-请求优化"><a href="#二、http-请求优化" class="headerlink" title="二、http 请求优化"></a>二、http 请求优化</h2><p>当域名解析成功后，浏览器就会和对应的服务器建立 TCP 连接，并发送 http 请求。http 请求可以优化的地方也有很多。</p>
<h3 id="减少-http-请求数"><a href="#减少-http-请求数" class="headerlink" title="减少 http 请求数"></a>减少 http 请求数</h3><p>能合并的文件<strong>尽量合并</strong>，比如使用雪碧图等都可以有效减少 http 请求数。</p>
<h3 id="TCP-连接复用-keep-alive"><a href="#TCP-连接复用-keep-alive" class="headerlink" title="TCP 连接复用 (keep-alive)"></a>TCP 连接复用 (keep-alive)</h3><p>HTTP 协议采用“请求-应答”模式。http1.0 时代，客户端每次请求都要和服务器新建一个 TCP 连接，完成之后立即断开连接。而使用 Keep-Alive 模式会使客户端到服务器端的 TCP 连接持续有效。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 启用Keep-Alive --&gt;</span><br><span class="line">Connection: Keep-Alive</span><br></pre></td></tr></table></figure>

<p>HTTP 1.1 时代，默认开启 Keep-Alive 模式，只有加入 <code>Connection: close</code>才关闭连接。Keep-Alive 模式的属性可以设置 ：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- timeout=6 表示这个 TCP 通道可以保持 6 秒，max=50 表示这个长连接最多接收 50 次请求就断开 --&gt;</span><br><span class="line">Keep-Alive: timeout=6, max=50，</span><br></pre></td></tr></table></figure>

<h3 id="浏览器请求阻塞"><a href="#浏览器请求阻塞" class="headerlink" title="浏览器请求阻塞"></a>浏览器请求阻塞</h3><p>浏览器在<strong>同一时刻对相同域名</strong>的请求数有一定的限制，不同的浏览器表现不同，一般都是 4-10 个。chrome 浏览器默认情况下是 6 个。为了绕过这个限制，可以采用<strong>多域名</strong>的方法。</p>
<h3 id="cookie-free"><a href="#cookie-free" class="headerlink" title="cookie-free"></a>cookie-free</h3><p><strong>浏览器不会把主域名的 Cookie 传给子域名</strong>。利用这一特性我们可以将 css 和 image 单独放在不同的子域名中，减少网络开销，一定程度提高了页面加载速度， 这种技术应用叫做 cookie-free。</p>
<h2 id="三、资源下载优化"><a href="#三、资源下载优化" class="headerlink" title="三、资源下载优化"></a>三、资源下载优化</h2><p>资源下载速度的快慢取决于文件的大小和服务器带宽，所以优化也是从这两方面入手。</p>
<ol>
<li>提高服务器 <strong>server 带宽</strong></li>
<li>开启服务器 <strong>gzip</strong> 压缩</li>
</ol>
<h2 id="四、浏览器渲染优化"><a href="#四、浏览器渲染优化" class="headerlink" title="四、浏览器渲染优化"></a>四、浏览器渲染优化</h2><p>浏览器资源渲染过程如下：</p>
<ol>
<li>解析 HTML 文件，构建 <strong>DOM 树</strong>，与此同时浏览器主进程下载 CSS 文件</li>
<li>CSS 文件下载完成，解析 CSS 文件构建 <strong>CSSOM 树</strong>；</li>
<li>CSSOM + DOM 合并成 <strong>render 树</strong>；</li>
<li>布局（<strong>Layout/reflow</strong>） render 树 ，负责 render 树中的元素的尺寸和位置等计算；</li>
<li>绘制（<strong>paint</strong>） render 树 ，绘制页面的像素信息（color 等）；</li>
<li>浏览器主进程将图层交给 GPU 进程，GPU 进程将各个<strong>图层合成</strong>（composite），最后显示出页面；</li>
</ol>
<p>由此可见：</p>
<ol>
<li>css 的加载<strong>不会影响 dom 的解析</strong>，但<strong>会影响 dom 的最终渲染</strong>。</li>
<li>当浏览器遇到 <code>&lt;script&gt;</code> 时，会<strong>阻止解析器</strong>继续执行。直到 <code>CSSOM 构建</code>完毕，JavaScript 才会运行并继续完成 <code>DOM 构建</code>过程。所以解析优先级 <code>CSSOM &gt; JS &gt; DOM</code>。</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;script src=&apos;&apos; async&gt;</span><br><span class="line">// async: 浏览器遇到带async的 script 标签时会继续解析 DOM，同时脚本也不会被 CSSOM 阻止，即不会阻止 CRP。</span><br><span class="line"></span><br><span class="line">&lt;script src=&apos;&apos; defer&gt;</span><br><span class="line">// defer: 脚本需要等到文档解析后（ **DOMContentLoaded 事件前**）执行，而 async 允许脚本在文档解析时位于后台运行（两者下载的过程都不会阻塞 DOM，但执行会）。</span><br><span class="line">// 当脚本不会修改 DOM 或 CSSOM 时，推荐使用 async 。</span><br></pre></td></tr></table></figure>

<p>了解以上原理后，我们就可以提出以下几点优化建议：</p>
<ol>
<li>合并重复的 css 样式，将 css 样式放在文档前部（如：<code>&lt;head&gt;中</code>）,将 js 放在文档后部（如：<code>&lt;body&gt;最后面</code>）；</li>
<li>只渲染<strong>首屏</strong>，页面采用<strong>懒加载</strong>方式；</li>
<li>有必要的地方采用页面<strong>预加载</strong>；</li>
</ol>
<h2 id="五、缓存优化"><a href="#五、缓存优化" class="headerlink" title="五、缓存优化"></a>五、缓存优化</h2><ol>
<li><p>DNS 缓存 （前面已经提到，此处略）</p>
</li>
<li><p>http 缓存<br>http 缓存主要缓存的对象是向服务器请求的 <strong>css/js/image</strong> 等文件。其主页入口<strong>html 文件</strong>一般不做缓存。下次请求时，缓存过的文件一般就不会向服务器发送请求。如果想解除缓存限制，只需要修改请求 url <strong>版本</strong>号（<code>MD5</code> 算法）。<br>添加缓存的方式：</p>
<ol>
<li><p>后端 response header 添加 <code>cache-control: max-age=1000</code>，此方法最常用；</p>
</li>
<li><p>Expires 和 Last-Modified，逐步被弃用；</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Expires:Thu, 02 Apr 2009 05:14:08 GMT</span><br><span class="line">Last-Modified:Tue, 24 Feb 2009 08:01:04 GMT</span><br></pre></td></tr></table></figure>
</li>
<li><p>ETAG，逐步被弃用<br><code>Etag:“5d8c72a5edda8d6a:3239″</code></p>
</li>
</ol>
</li>
</ol>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2021/01/03/web性能优化/" data-id="ckjqth4rz002f2gw01p6jljey" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/性能优化/">性能优化</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-nodejs入门" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/12/31/nodejs入门/" class="article-date">
  <time datetime="2020-12-31T06:44:52.000Z" itemprop="datePublished">2020-12-31</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nodejs/">nodejs</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/12/31/nodejs入门/">nodejs 入门</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="什么是-nodejs"><a href="#什么是-nodejs" class="headerlink" title="什么是 nodejs?"></a>什么是 nodejs?</h2><p>对于新手而言，nodejs 可能会<strong>被误认为是后端的 js</strong>。我们要知道，nodejs <strong>不是一个 web 框架</strong>，也<strong>不是一门编程语言</strong>，它是一个让 js 也可以调用系统接口，开发后端应用的<strong>运行环境</strong>。</p>
<h2 id="nodejs-技术架构"><a href="#nodejs-技术架构" class="headerlink" title="nodejs 技术架构"></a>nodejs 技术架构</h2><p>我们知道了 nodejs 是一个平台。那么它底层的技术架构是怎么实现的呢？<br><img src="/images/nodejs_process.jpg" alt="nodejs 工作流程"><br>nodejs 给用户提供了常用的 <strong>API</strong> 。这些 api 我们可以直接在 js 中使用。js 是运行在<strong>V8 引擎</strong>上的，V8 引擎通过 <strong>nodejs Bindings</strong> 与 <strong>libUV</strong> 通信。除此之外，我们还可以<strong>自定义 c/c++ 插件</strong>拓展功能。</p>
<h3 id="nodejs-Bindings"><a href="#nodejs-Bindings" class="headerlink" title="nodejs Bindings"></a>nodejs Bindings</h3><p>我们都知道 c/c++执行效率非常高，那么我们想用 js 直接调用 c/c++库 怎么办？nodejs Bindings 就是连接 js 和 c/c++ 的中间桥梁。Binding 将 c/c++ 编译为 .node 文件，这个文件可以直接被 js 获取并执行。</p>
<h3 id="V8-引擎"><a href="#V8-引擎" class="headerlink" title="V8 引擎"></a>V8 引擎</h3><p>V8 引擎负责将 js 源代码变成本地代码并执行。它<strong>维护了调用栈</strong>，确保 js 的执行顺序。负责<strong>内存管理和垃圾回收</strong>。实现了 js 的标准库，但不提供 DOM API, DOM API 是浏览器提供的。<br>V8 引擎本身是多线程的，但是执行 js 是单线程，它自身有 EventLoop 机制，但是 <strong>nodejs 的 EventLoop 是基于 libuv 的</strong>。</p>
<h3 id="libUV"><a href="#libUV" class="headerlink" title="libUV"></a>libUV</h3><p>libUV 是 nodejs 之父 Ryan 开发的一个<strong>高性能跨平台 异步 I/O 库</strong>，底层由 c 语言编写，有自己的一套 eventLoop 机制。它专为 Node.js 而设计，后因<strong>事件驱动</strong>的异步 IO 的高效逐渐被其他语言和项目采纳。libUV 通常可用于 TCP / UDP / DNS / FileSystem 等异步操作。</p>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/12/31/nodejs入门/" data-id="ckjqth4rk001m2gw0yi46b6th" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/nodejs/">nodejs</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-vue单页面应用白屏屏优化" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/12/27/vue单页面应用白屏屏优化/" class="article-date">
  <time datetime="2020-12-27T02:08:15.000Z" itemprop="datePublished">2020-12-27</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/vue/">vue</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/12/27/vue单页面应用白屏屏优化/">vue单页面应用白屏屏优化</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="什么是单页面应用（SPA-？"><a href="#什么是单页面应用（SPA-？" class="headerlink" title="什么是单页面应用（SPA)？"></a>什么是单页面应用（SPA)？</h2><p>vue 单页面应用使前端开发更加快捷、方便。<strong>何谓单页面应用（SPA)</strong>？</p>
<p><strong>单页面</strong>应用是指<strong>只有一个主页面</strong>的应用，浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。在交互的时候由路由程序动态载入，单页面的页面跳转，仅局部刷新。</p>
<p><strong>多页面</strong>应用（MPA）是指有多个页面的应用，页面跳转时是整页刷新。</p>
<h2 id="单页面应用的优缺点"><a href="#单页面应用的优缺点" class="headerlink" title="单页面应用的优缺点"></a>单页面应用的优缺点</h2><h3 id="优点："><a href="#优点：" class="headerlink" title="优点："></a>优点：</h3><ol>
<li>页面跳转快而流畅，可添加转场效果，局部刷新用户体验较好。</li>
<li>前后端分离，组件化开发，易复用，易维护。</li>
<li>页面跳转基于前端路由，对服务器压力较小。</li>
</ol>
<h3 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h3><ol>
<li>首页加载仅一个空标签，对<strong>SEO 不友好</strong>。</li>
<li>页面基于 js 渲染，弱网环境下，首页加载时间长，会出现<strong>白屏问题</strong>。</li>
<li>页面导航需要自行实现，页面的复杂度较高，需要一定的开发成本。</li>
</ol>
<h2 id="单页面应用-SEO-和白屏优化"><a href="#单页面应用-SEO-和白屏优化" class="headerlink" title="单页面应用 SEO 和白屏优化"></a>单页面应用 SEO 和白屏优化</h2><p>我们可以通过按需加载、gzip 压缩等方法缓解单页面应用 js 包过大问题。但是解决白屏优化主要有以下三种方法：</p>
<ol>
<li><strong>骨架屏</strong> (优化白屏问题)</li>
<li><strong>预渲染</strong> (preRender 优化 SEO)</li>
<li><strong>服务端渲染</strong>（ssr 优化白屏问题和 SEO)</li>
</ol>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/12/27/vue单页面应用白屏屏优化/" data-id="ckjqth4rz00222gw0x11cy64s" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/性能优化/">性能优化</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-逸辰音乐问题点" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/12/26/逸辰音乐问题点/" class="article-date">
  <time datetime="2020-12-26T12:09:14.000Z" itemprop="datePublished">2020-12-26</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/前端/">前端</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/12/26/逸辰音乐问题点/">逸辰音乐问题点</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>今天在重构自己的项目时，发现了不少问题。总结归纳一下， 以便提高自己。</p>
<h2 id="一、在-vue-项目现有的-stylus-代码中添加新的样式无效"><a href="#一、在-vue-项目现有的-stylus-代码中添加新的样式无效" class="headerlink" title="一、在 vue 项目现有的 stylus 代码中添加新的样式无效"></a>一、在 vue 项目现有的 stylus 代码中添加新的样式无效</h2><p>在重构旧项目的过程中，我需要在现有 stylus 样式中添加一些 css。但是在添加 css 后，发现新样式不仅没有生效，反而会影响原有的样式。绞尽脑汁，最后发现是<strong>stylus 缩进的格式</strong>有问题。stylus 写法有很多种，常见的写法有 <strong>{}</strong> 或者<strong>缩进</strong>。当使用缩进写法时，<strong>空格和 tab 不能混用</strong>，否则会造成编译错误。<br><img src="/images/stylus_bug_reason.png" alt="stylus"></p>
<h2 id="二、多个-jsonp-请求问题"><a href="#二、多个-jsonp-请求问题" class="headerlink" title="二、多个 jsonp 请求问题"></a>二、多个 jsonp 请求问题</h2><p>使用 jsonp 库<strong>同时发送多个 jsonp</strong>请求时，要保证 <strong>callback 参数各不相同</strong>，否则会导致 data 解析失败。</p>
<h2 id="三、页面刷新-vuex-数据丢失"><a href="#三、页面刷新-vuex-数据丢失" class="headerlink" title="三、页面刷新 vuex 数据丢失"></a>三、页面刷新 vuex 数据丢失</h2><p>在项目中，我们很多时候不可避免的会使用 vuex 来管理全局状态。vuex 状态是保存在<strong>运行时内存</strong>中的。当<strong>F5</strong>页面刷新时，vuex 会被<strong>新建并初始化</strong>，这就导致了数据丢失。常用的解决办法就是借助 <strong>sessionStorage</strong> 或者 <strong>localStorage</strong> 缓存 vuex 中重要的数据。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">//  in state.ts</span><br><span class="line">const sessionDisc = window.sessionStorage.getItem(&quot;disc&quot;)</span><br><span class="line">const state: any = &#123;</span><br><span class="line">  disc: sessionDisc ? JSON.parse(sessionDisc) : &#123;&#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">//  in mutations.ts</span><br><span class="line">  [types.SET_DISC](state: any, cd: any) &#123;</span><br><span class="line">    state.disc = cd</span><br><span class="line">    window.sessionStorage.setItem(&quot;disc&quot;, JSON.stringify(cd))</span><br><span class="line">  &#125;,</span><br></pre></td></tr></table></figure>


      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/12/26/逸辰音乐问题点/" data-id="ckjqth4sz003t2gw0ry7eef78" class="article-share-link">Share</a>-->
      
      
    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-造轮子遇到的问题" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/12/24/造轮子遇到的问题/" class="article-date">
  <time datetime="2020-12-24T10:53:15.000Z" itemprop="datePublished">2020-12-24</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/Vue/">Vue</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/12/24/造轮子遇到的问题/">造轮子遇到的问题</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>在 vue3 造轮子的过程中，我遇到了很多问题，这都是在平时在写业务代码时不常见的知识点。在写业务代码时，我主要关注的是<strong>业务逻辑</strong>，写出的代码质量相对不高。而在造轮子的过程中，我需要去抽象各种组件，会遇到各种形形色色的问题。总结归纳有助于不断的提高自己。</p>
<h3 id="一、-使用-BackTop-组件传递-target（滚动容器）"><a href="#一、-使用-BackTop-组件传递-target（滚动容器）" class="headerlink" title="一、 使用 BackTop 组件传递 target（滚动容器）"></a>一、 使用 BackTop 组件传递 target（滚动容器）</h3><p>当我们浏览 web 页面过长时，手动滚动回顶部需要的时间较长，这时可能就需要<strong>返回页面顶部</strong>的操作按钮。所以我就自己写了一个 BackTop 组件，该组件的具体用法<a href="https://cai4633.gitee.io/mom-ui-doc/#/doc/backtop" target="_blank" rel="noopener">见文档</a>。BackTop 组件使用时需要传递一个 target，这个参数是被监视滚动事件的元素，默认是 document。<br>一开始，我设计的是直接传递一个 ELEMENT_NODE 给 target。但在实际运行时，发现会报错。因为在<strong>ELEMENT_NODE</strong>被传递过来时，<strong>ELEMENT_NODE 不一定已经被渲染</strong>。</p>
<p>解决这个问题的方法很经典：<strong>使用函数延迟代码执行</strong>。我不再直接传递 ELEMENT_NODE ， 而是传递一个函数给 target。 该<strong>函数的返回值是一个 ELEMENT_NODE</strong> 。这样当我在 BackTop 组件 onMounted()时调用这个函数就可以适时的获取想要的 target。</p>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/12/24/造轮子遇到的问题/" data-id="ckjqth4si003i2gw06n0bjdyl" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Vue3/">Vue3</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-ES6-常用知识点" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/12/16/ES6-常用知识点/" class="article-date">
  <time datetime="2020-12-16T04:42:03.000Z" itemprop="datePublished">2020-12-16</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/ES6/">ES6</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/12/16/ES6-常用知识点/">ES6 常用知识点</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <ol>
<li><p>let 和 const 命令</p>
</li>
<li><p>解构赋值<br>ES6 允许按照一定模式，从数组和对象中提取值，对变量进行赋值，这被称为<strong>解构</strong>（Destructuring）。<br>解构赋值允许指定<strong>默认值</strong>，只有当一个成员<strong>严格等于undefined</strong>，默认值才会生效。<br>解构赋值在变量声明时不要添加<strong>圆括号</strong>，在变量赋值时可以添加圆括号。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">// 错误的写法</span><br><span class="line">let x;</span><br><span class="line">&#123;x&#125; = &#123;x: 1&#125;; // SyntaxError: syntax error</span><br><span class="line"></span><br><span class="line">// 正确的写法</span><br><span class="line">let x;</span><br><span class="line">(&#123;x&#125; = &#123;x: 1&#125;);</span><br></pre></td></tr></table></figure>

<ul>
<li>数组的解构赋值<br>只要某种数据结构具有 <strong>Iterator 接口</strong>，都可以采用数组形式的解构赋值。（例如：Array 、 Set 、Map 和  Generator 函数）</li>
<li>对象的解构赋值<br>对象的解构赋值的内部机制，是<strong>先找到同名属性</strong>，然后<strong>再赋值给对应的变量</strong>。真正被赋值的是后者，而不是前者。</li>
<li>函数参数的解构赋值 </li>
<li>字符串的解构赋值<br><code>const [a,b] = &#39;xy&#39; // a=&#39;x&#39;; b=&#39;y&#39;</code></li>
<li>数值和布尔值的解构赋值<br>只要等号右边的值<strong>不是对象或数组</strong>，就先将其<strong>转为对象</strong>。由于<strong>undefined和null无法转为对象</strong>，所以对它们进行解构赋值，都会报错。</li>
</ul>
</li>
<li><p>模板字符串 和 标签模板</p>
</li>
</ol>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/12/16/ES6-常用知识点/" data-id="ckjqth4qo000g2gw08d8n6mdk" class="article-share-link">Share</a>-->
      
      
    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-var-let-和-const-的区别" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/12/16/var-let-和-const-的区别/" class="article-date">
  <time datetime="2020-12-16T02:10:14.000Z" itemprop="datePublished">2020-12-16</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/ES6/">ES6</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/12/16/var-let-和-const-的区别/">var 、 let 和 const</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="var-和-let-的区别"><a href="#var-和-let-的区别" class="headerlink" title="var 和 let 的区别"></a>var 和 let 的区别</h2><ol>
<li><strong>作用域不同</strong>。let 有块级作用域。var 只有全局作用域和函数作用域。（例如：for 代码块）js 的作用域只是定义了<strong>变量的可访问性</strong>，但是变量的具体值决定于<strong>代码（函数）执行时机。</strong></li>
<li><strong>变量提升和暂时性死区</strong>。 var 存在变量提升。let 不存在变量提升但是有暂时性死区。</li>
<li><strong>重复声明</strong>。let 不允许在相同作用域内，重复声明同一个变量。var 可以。</li>
<li><strong>全局对象属性</strong>。 var 声明的变量是全局变量的属性，let 声明的变量却不是。</li>
</ol>
<h2 id="块级作用域"><a href="#块级作用域" class="headerlink" title="块级作用域"></a>块级作用域</h2><p>ES5 中只存在全局作用域和函数作用域，而 ES6 中添加了块级作用域。块级作用域几个易错点：</p>
<ul>
<li>块级作用域必须要有<strong>大括号</strong>。</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">// 第一种写法，报错</span><br><span class="line">if (true) let x = 1;</span><br><span class="line"></span><br><span class="line">// 第二种写法，不报错</span><br><span class="line">if (true) &#123;</span><br><span class="line">  let x = 1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>由于不同浏览器环境对于<strong>函数声明在块级作用域中的行为</strong>差别较大，所以块级作用域中的函数声明最好用<strong>函数表达式来代替</strong>。</li>
</ul>
<h2 id="const"><a href="#const" class="headerlink" title="const"></a>const</h2><p><strong>const</strong> 声明一个<strong>只读</strong>的常量。声明变量时，就必须立即<strong>初始化</strong>。</p>
<p>const 和 let 很多特性类似，包括块级作用域、无变量提升、有暂时性死区、不能重复声明和不绑定全局对象属性。</p>
<p>const 实际上保证的，并不是变量的值不得改动，而是变量指向的那个内存地址所保存的数据不得改动。如果真的想将对象冻结，应该使用<strong>Object.freeze</strong>方法。</p>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/12/16/var-let-和-const-的区别/" data-id="ckjqth4rk001t2gw0e9y3camb" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/const/">const</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/let/">let</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  


  <nav id="page-nav">
    
    <span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="page-number" href="/page/3/">3</a><span class="space">&hellip;</span><a class="page-number" href="/page/6/">6</a><a class="extend next" rel="next" href="/page/2/">下一页 &raquo;</a>
  </nav>

</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">最新文章</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2021/01/08/前端鉴权总结/">前端鉴权总结</a>
          </li>
        
          <li>
            <a href="/2021/01/03/web性能优化/">web性能优化</a>
          </li>
        
          <li>
            <a href="/2020/12/31/nodejs入门/">nodejs 入门</a>
          </li>
        
          <li>
            <a href="/2020/12/27/vue单页面应用白屏屏优化/">vue单页面应用白屏屏优化</a>
          </li>
        
          <li>
            <a href="/2020/12/26/逸辰音乐问题点/">逸辰音乐问题点</a>
          </li>
        
      </ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">归档</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/01/">一月 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/12/">十二月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/09/">九月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/08/">八月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/07/">七月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/05/">五月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/04/">四月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/03/">三月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/02/">二月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/11/">十一月 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/10/">十月 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/07/">七月 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/06/">六月 2019</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">分类</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/ES6/">ES6</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Vue/">Vue</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/css/">css</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/git/">git</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/html/">html</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/js/">js</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/nodejs/">nodejs</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/vue/">vue</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/前端/">前端</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/前端项目/">前端项目</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/服务端/">服务端</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/算法/">算法</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">标签</h3>
    <div class="widget">
      <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/Apache/">Apache</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Array/">Array</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/BFC/">BFC</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/CORS/">CORS</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/DOM/">DOM</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/DOM-diff/">DOM diff</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/JSONP/">JSONP</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Vue3/">Vue3</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/absolute/">absolute</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ajax/">ajax</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/auto/">auto</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/background/">background</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/const/">const</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/devServer/">devServer</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/function/">function</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/">git</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/github/">github</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hexo/">hexo</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/host/">host</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/html/">html</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/http/">http</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jquery/">jquery</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/js/">js</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/leancloud/">leancloud</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/let/">let</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nodejs/">nodejs</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/npm/">npm</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/position/">position</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/promise/">promise</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/proxy/">proxy</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/script标签/">script标签</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/session-cookie/">session-cookie</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/token/">token</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/v-if/">v-if</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/v-show/">v-show</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vDOM/">vDOM</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vHost/">vHost</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vue/">vue</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vue-cli/">vue-cli</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vuec/">vuec</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vue生命周期/">vue生命周期</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/width/">width</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/z-index/">z-index</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/原型链/">原型链</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/同源/">同源</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/外边距/">外边距</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/层叠上下文/">层叠上下文</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/性能优化/">性能优化</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/数据结构/">数据结构</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/浏览器/">浏览器</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/百分比/">百分比</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/算法/">算法</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/精华/">精华</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/软件下载/">软件下载</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/鉴权/">鉴权</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">标签云</h3>
    <div class="widget tagcloud">
      <a href="/tags/Apache/" style="font-size: 10px;">Apache</a> <a href="/tags/Array/" style="font-size: 10px;">Array</a> <a href="/tags/BFC/" style="font-size: 10px;">BFC</a> <a href="/tags/CORS/" style="font-size: 10px;">CORS</a> <a href="/tags/DOM/" style="font-size: 10px;">DOM</a> <a href="/tags/DOM-diff/" style="font-size: 10px;">DOM diff</a> <a href="/tags/JSONP/" style="font-size: 10px;">JSONP</a> <a href="/tags/Vue3/" style="font-size: 10px;">Vue3</a> <a href="/tags/absolute/" style="font-size: 10px;">absolute</a> <a href="/tags/ajax/" style="font-size: 10px;">ajax</a> <a href="/tags/auto/" style="font-size: 10px;">auto</a> <a href="/tags/background/" style="font-size: 10px;">background</a> <a href="/tags/const/" style="font-size: 10px;">const</a> <a href="/tags/devServer/" style="font-size: 10px;">devServer</a> <a href="/tags/function/" style="font-size: 10px;">function</a> <a href="/tags/git/" style="font-size: 10px;">git</a> <a href="/tags/github/" style="font-size: 10px;">github</a> <a href="/tags/hexo/" style="font-size: 10px;">hexo</a> <a href="/tags/host/" style="font-size: 10px;">host</a> <a href="/tags/html/" style="font-size: 10px;">html</a> <a href="/tags/http/" style="font-size: 16.67px;">http</a> <a href="/tags/jquery/" style="font-size: 10px;">jquery</a> <a href="/tags/js/" style="font-size: 20px;">js</a> <a href="/tags/leancloud/" style="font-size: 10px;">leancloud</a> <a href="/tags/let/" style="font-size: 10px;">let</a> <a href="/tags/nodejs/" style="font-size: 10px;">nodejs</a> <a href="/tags/npm/" style="font-size: 10px;">npm</a> <a href="/tags/position/" style="font-size: 10px;">position</a> <a href="/tags/promise/" style="font-size: 10px;">promise</a> <a href="/tags/proxy/" style="font-size: 10px;">proxy</a> <a href="/tags/script标签/" style="font-size: 10px;">script标签</a> <a href="/tags/session-cookie/" style="font-size: 10px;">session-cookie</a> <a href="/tags/token/" style="font-size: 10px;">token</a> <a href="/tags/v-if/" style="font-size: 10px;">v-if</a> <a href="/tags/v-show/" style="font-size: 10px;">v-show</a> <a href="/tags/vDOM/" style="font-size: 10px;">vDOM</a> <a href="/tags/vHost/" style="font-size: 10px;">vHost</a> <a href="/tags/vue/" style="font-size: 13.33px;">vue</a> <a href="/tags/vue-cli/" style="font-size: 10px;">vue-cli</a> <a href="/tags/vuec/" style="font-size: 10px;">vuec</a> <a href="/tags/vue生命周期/" style="font-size: 10px;">vue生命周期</a> <a href="/tags/width/" style="font-size: 10px;">width</a> <a href="/tags/z-index/" style="font-size: 10px;">z-index</a> <a href="/tags/原型链/" style="font-size: 10px;">原型链</a> <a href="/tags/同源/" style="font-size: 10px;">同源</a> <a href="/tags/外边距/" style="font-size: 10px;">外边距</a> <a href="/tags/层叠上下文/" style="font-size: 10px;">层叠上下文</a> <a href="/tags/性能优化/" style="font-size: 13.33px;">性能优化</a> <a href="/tags/数据结构/" style="font-size: 10px;">数据结构</a> <a href="/tags/浏览器/" style="font-size: 16.67px;">浏览器</a> <a href="/tags/百分比/" style="font-size: 10px;">百分比</a> <a href="/tags/算法/" style="font-size: 10px;">算法</a> <a href="/tags/精华/" style="font-size: 10px;">精华</a> <a href="/tags/软件下载/" style="font-size: 10px;">软件下载</a> <a href="/tags/鉴权/" style="font-size: 10px;">鉴权</a>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      &copy; 2021 <a href="http://github.com/cai4633/cai4633.github.io" target="_blank">Cai4633</a><br>
      <!--Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>-->
    </div>
  </div>
</footer>
    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/2.0.3/jquery.min.js"></script>


  <link rel="stylesheet" href="/fancybox/jquery.fancybox.css">
  <script src="/fancybox/jquery.fancybox.pack.js"></script>


<script src="/js/script.js"></script>



    <script>
      let isMobile = false
      if (/Android|webOS|iPhone|iPad|BlackBerry/i.test(navigator.userAgent)) {
        isMobile = true
        $("#nav").addClass("is-mobile")
        $("footer").addClass("is-mobile")
      }
      if ($(".local-search").size()) {
        $.getScript("/js/search.js", function () {
          searchFunc("/search.xml", "local-search-input", "local-search-result")
        })
      }
    </script>
  </div>
</body>
</html>